﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace Lilium.Web.Mvc
{
	public static class ModelMetadataExtensions
	{
		public static void AddExtension<TExtension>(this ModelMetadata modelMetadata, TExtension extension)
		{
			AddExtension(modelMetadata, extension, typeof(TExtension), null);
		}

		public static void AddExtension<TExtension>(this ModelMetadata modelMetadata, TExtension extension, string name)
		{
			AddExtension(modelMetadata, extension, typeof(TExtension), name);
		}

		public static void AddExtension(this ModelMetadata modelMetadata, object extension, Type type, string name)
		{
			if (modelMetadata == null) throw new ArgumentNullException("modelMetadata");
			if (extension == null) throw new ArgumentNullException("extension");
			if (type == null) throw new ArgumentNullException("type");

            var key = GetStringKey(Tuple.Create(type, name)); 
			modelMetadata.AdditionalValues.Add(key, extension);
		}

		public static TExtension FindExtension<TExtension>(this ModelMetadata modelMetadata)
			where TExtension : class
		{
			return FindExtension<TExtension>(modelMetadata, null);
		}

		public static TExtension FindExtension<TExtension>(this ModelMetadata modelMetadata, bool required)
			where TExtension : class
		{
			var extension = FindExtension<TExtension>(modelMetadata, null);
			if (required && (extension == null))
				throw new InvalidOperationException(string.Format("Missing required metadata extension {0}", typeof(TExtension)));
			return extension;
		}

		public static TExtension FindExtension<TExtension>(this ModelMetadata modelMetadata, string name)
		{
			if (modelMetadata == null) throw new ArgumentNullException("modelMetadata");

			object extension;
            var strictKey = GetStringKey(Tuple.Create(typeof(TExtension), name));

			if (modelMetadata.AdditionalValues.TryGetValue(strictKey, out extension))
				return (TExtension)extension;

			if (name != null)
			{
                var defaultKey = GetStringKey(Tuple.Create(typeof(TExtension), (string)null)); 
				if (modelMetadata.AdditionalValues.TryGetValue(defaultKey, out extension))
					return (TExtension)extension;
			}

			return default(TExtension);
		}

		public static void RemoveExtension<TExtension>(this ModelMetadata modelMetadata, TExtension extension)
		{
			if (modelMetadata == null) throw new ArgumentNullException("modelMetadata");
			if (extension == null) throw new ArgumentNullException("extension");

			var extensionKeys = modelMetadata.AdditionalValues.Where(p => extension.Equals(p.Value)).Select(p => p.Key).ToArray();
			foreach (var key in extensionKeys)
				modelMetadata.AdditionalValues.Remove(key);
		}

        private static  string GetStringKey(Tuple<Type, string> key)
        {
            var result = key.Item1.FullName;
            if (!string.IsNullOrEmpty(key.Item2))
                result += "+" + key.Item2;
            return result;
        }
	}
}
